package com.block.source.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.block.common.enums.InfoTypeEnum;
import com.block.common.exception.NotifyExceptionService;
import com.block.common.model.dto.RequestTimeDTO;
import com.block.common.model.vo.ResultInfoVO;
import com.block.common.service.*;
import com.block.db.entity.*;
import com.block.merchant.service.IMerchantBaseInfoService;
import com.block.source.enums.ReceiveTypeEnum;
import com.block.source.factory.SourceApiFactory;
import com.block.source.model.dto.SourceApiParam;
import com.block.source.model.dto.SourceApiResult;
import com.block.source.model.param.UserDataParam;
import com.block.source.service.ISourceApiCheckUserService;
import com.block.source.service.ISourceCheckUserService;
import com.block.source.service.ISourcePushResultInfoService;
import com.block.source.service.ISourceReceiveService;
import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Service
public class SourceApiCheckUserService implements ISourceApiCheckUserService {

    private static final Logger log = LogFactory.getSourceLog();

    @Autowired
    ISourceBaseInfoService sourceBaseInfoService;
    @Autowired
    ISourceCheckUserService sourceCheckUserService;
    @Autowired
    IChannelCommonService channelCommonService;
    @Autowired
    IApplicationInfoService applicationInfoService;
    @Autowired
    ISourcePushResultInfoService sourcePushResultInfoService;
    @Autowired
    IMerchantBaseInfoService merchantBaseInfoService;
    @Autowired
    NotifyExceptionService notifyExceptionService;
    @Autowired
    ISystemRequestTimeRecordService systemRequestTimeRecordService;

    private final RateLimiter rateLimiter = RateLimiter.create(20);

    private static final List<String> openChannelCode = Collections.unmodifiableList(
            Arrays.asList("ZIUFGV", "SSB093", "4OR1UZ", "2LW530","SEX3ID")
    );

    @Override
    public JSONObject apiCheckUser(String channelCode, Map<String, String[]> parameterMap, String paramContent, String requestIp){

        String message = StrUtil.format("【线索api撞库】请求数据展示【channelCode】{}【parameterMap】{}【paramContent】{}", channelCode, JSON.toJSONString(parameterMap), paramContent);
        log.info(message);

        try {

            Long startTime = System.currentTimeMillis();

            // 查询基本信息
            SourceBaseInfo sourceBaseInfo = sourceBaseInfoService.getSourceBaseInfoByCode(channelCode);
            if (ObjectUtil.isEmpty(sourceBaseInfo)){
                JSONObject error = SourceApiFactory.getService(ReceiveTypeEnum.DEFAULT.getValue()).getError("渠道信息为空");
                return error;
            }
            /**
             * 获取线索 对应的 处理类
             */
            ISourceReceiveService sourceReceiveService = SourceApiFactory.getService(sourceBaseInfo.getFactoryType());
            if (!ObjectUtil.equals(sourceBaseInfo.getStatus(), Integer.valueOf(1))){
                JSONObject error = sourceReceiveService.getError("渠道信息不可用");
                return error;
            }
            if (!ObjectUtil.equals(sourceBaseInfo.getType(), InfoTypeEnum.APIS.getValue())){
                JSONObject error = sourceReceiveService.getError("渠道类型错误，非双授权");
                return error;
            }

            if (!openChannelCode.contains(channelCode)) {
                boolean limiter = rateLimiter.tryAcquire();
                if (!limiter){
//                    JSONObject returnObject = new JSONObject();
//                    returnObject.put("code", "500");
//                    returnObject.put("message", "请求频繁，稍后再试");
//                    return returnObject.toJSONString();
                    return sourceReceiveService.getError("撞库失败");
                }
            }

            // 基本信息判断
            // 根据请求信息，构造需要的参数信息
            SourceApiResult sourceApiResult = sourceReceiveService.handleCheckUserData(sourceBaseInfo, parameterMap, paramContent);
            log.info("【线索api撞库】撞库数据转换完毕【channelCode】{}【sourceApiResult】{}", channelCode, JSON.toJSONString(sourceApiResult));
            if (!sourceApiResult.isSuccess()){
                return sourceApiResult.getReturnObject();
            }

            // 执行撞库操作
            UserDataParam userDataParam = sourceApiResult.getUserDataParam();
            userDataParam.setRequestIp(requestIp);

            // 撞库参数校验
            String respMsg = ISourceReceiveService.verifyCheckUserParam(userDataParam);
            // 返回信息不为空，则有异常，直接返回
            if (StrUtil.isNotEmpty(respMsg)) {
                log.info("【线索api撞库】撞库数据检验【channelCode】{}【verifyRes】{}", channelCode, respMsg);
                return sourceReceiveService.getError(respMsg);
            }
            //
            ChannelBaseInfo channelBaseInfo = channelCommonService.getBaseInfoByCode(channelCode);
            ApplicationInfo applicationInfo = applicationInfoService.getAppInfoByAppCode(channelBaseInfo.getAppCode());

            ResultInfoVO resultInfoVO = sourceCheckUserService.sourceCheckUser1(sourceBaseInfo, userDataParam, channelBaseInfo, applicationInfo);
            log.info("【线索api撞库】数据撞库完成【channelCode】{}【resultInfoVO】{}", channelCode, JSON.toJSONString(resultInfoVO));
            if (!resultInfoVO.isSuccess()){
                return sourceReceiveService.getError(resultInfoVO.getRespMsg());
            }

            // 根据返回结果，构造渠道方需要的数据
            JSONObject data = resultInfoVO.getData();
            String status = data.getString("status");
            if (!StrUtil.equals(status, "000")){
                return sourceReceiveService.getError("撞库失败");
            }

            String orderNo = data.getString("orderNo");
            SourcePushResultInfo sourcePushResultInfo = sourcePushResultInfoService.getById(orderNo);
            MerchantBaseInfo merchantBaseInfo = merchantBaseInfoService.getById(sourcePushResultInfo.getInfoId());

            MerchantThirdBaseInfo thirdBaseInfo = data.getObject("thirdBaseInfo", MerchantThirdBaseInfo.class);
            List<MerchantThirdProtocolInfo> thirdProtocolInfoList = data.getObject("thirdProtocolInfoList", List.class);

            SourceApiParam sourceApiParam = new SourceApiParam();
            sourceApiParam.setSourcePushResultInfo(sourcePushResultInfo);
            sourceApiParam.setMerchantBaseInfo(merchantBaseInfo);
            sourceApiParam.setThirdBaseInfo(thirdBaseInfo);
            sourceApiParam.setThirdProtocolInfoList(thirdProtocolInfoList);
            sourceApiParam.setExtObject(sourceApiResult.getExtObject());

            // 根据请求信息，构造需要的参数信息
            SourceApiResult checkUserResult = sourceReceiveService.getCheckUserReturnData(sourceBaseInfo, resultInfoVO, sourceApiParam);

            Long execTime = System.currentTimeMillis() - startTime;
            // 返回数据
            log.info("【线索api撞库】请求完成，返回撞库数据【channelCode】{}【execTime】{}【checkUserResult】{}", channelCode, execTime, JSON.toJSONString(checkUserResult));


            RequestTimeDTO timeDTO = new RequestTimeDTO();
            timeDTO.setBizType("SOURCE_REQUEST_RESPONSE_APIS");
            timeDTO.setBizName("线索APIS请求响应");
            timeDTO.setExecTime(String.valueOf(execTime ));
            timeDTO.setLineTime("500");
            timeDTO.setParamInfo(JSON.toJSONString(sourceApiResult));
            timeDTO.setRemarkInfo(StrUtil.format("【channelCode】{}【channelName】{}", sourceBaseInfo.getChannelCode(), sourceBaseInfo.getChannelName()));
            if (execTime > 2000){
                systemRequestTimeRecordService.saveRequestTime(timeDTO);
            }

            return checkUserResult.getReturnObject();
        }catch (Exception exception){
            exception.printStackTrace();
            notifyExceptionService.notifyNoticeInfo("SOURCE_API_CHECK_EXEC", "线索api撞库异常", message, exception);
        }
        return SourceApiFactory.getService(ReceiveTypeEnum.DEFAULT.getValue()).getError("撞库异常");
    }

}
